import re
import math
from handle.template_conf import *
from yxlog.logger import get_logger
from handle.multi_line_template import MergeParser
from handle.base import ComparisonPosition, SingleTextPosition
from handle.parse_config import *
from collections import Counter

logger = get_logger(level="INFO")


class TemplateParser(object):
    """
    模板解析
    """

    def __init__(self, sorted_func=None, special_conf_name="common_template", **kwargs):
        """
        text_line_points 文本行矩形框点位置  example:
        [
            {"text": "营业执照", "score": 0.635, "bbox": [725, 445, 1758, 445, 1758, 731, 725, 731]},
            {"text": "统一社会信用代码", "score": 0.75, "bbox": [225, 533, 581, 533, 581, 571, 225, 571]}
            {"text": "录“国", "score": 0.632, "bbox": [2178, 540, 2233, 540, 2233, 564, 2178, 564]}
        ]
        config_name 指定配置dict
        """
        self._ownFields = ["createDate"]
        self.requiredFields = kwargs["requiredFields"]
        self.optionalFields = kwargs["optionalFields"]
        self._certificateName = kwargs["certificateName"]
        self._subCertificateName = kwargs["subCertificateName"]
        self._certificateType = kwargs["certificateType"]
        self._image = kwargs["image"]
        self.usedIds = kwargs["usedIds"]
        self._specialChars = [":", "：", ";", "。", "，", "*", "\\", "\""]
        self.config = self.__load_config(special_conf_name)
        self._sorted_func = sorted_func
        self._sorted_ocr_result = self.__sort_text_line_pos(kwargs["ocrResult"])
        self._len_rect = len(self._sorted_ocr_result)
        self._avg_height = sum([item.height for item in self._sorted_ocr_result]) / self._len_rect

        self._char_max_count = 5
        self._max_height_times = 2  # 最大文本高度比例

    @staticmethod
    def __load_config(special_conf_name):
        special_dict = eval(special_conf_name)
        if special_conf_name == "common_template":
            return special_dict

        special_common = special_dict.get("common_template", dict())
        for key, val in common_template.items():
            special_common[key] = special_common.get(key, val)

            assert isinstance(val, type(special_common[key]))
            if isinstance(val, (int, float)):
                continue
            elif isinstance(val, dict):
                for inner_key, inner_val in val.items():
                    special_common[key][inner_key] = special_common[key].get(inner_key, inner_val)
            else:
                raise Exception("special common template type is error")

        special_dict.get("common_template", dict()).update(special_common)
        return special_dict

    def __sort_text_line_pos(self, text_line_pos):
        """
        文本框简单排序  可优化
        """
        handle_position = [SingleTextPosition(item) for item in text_line_pos]
        if not self._sorted_func:
            return handle_position
        return self._sorted_func(handle_position)

    def __is_abnormal__ratio(self, cur_pos):
        """异常长宽比例"""
        # 1. 高度大于宽度
        if cur_pos.height >= cur_pos.width:
            return True

        # 2. 角度大于等于10度
        tanh_val = (cur_pos.srcBbox[7] - cur_pos.srcBbox[5]) / (cur_pos.srcBbox[4] - cur_pos.srcBbox[6])
        degree = math.degrees(math.atan(tanh_val))
        if abs(degree) >= 10:
            return True

        # 3. 异常大的文本框
        if cur_pos.height / self._avg_height > self._max_height_times:
            return True

        # 4. 重复字符很多，代表识别异常
        _char, count = Counter(cur_pos.text).most_common()[0]
        if re.search("[a-zA-Z]", _char) and count >= self._char_max_count:
            return True
        return False

    @staticmethod
    def __is_irrelevant(cur_text):
        pattern = "attp:|共和国.*行政管理.*制|企业.*公示.*|国家企业信用|公示网|http|国家.*监督管理"
        if re.search(pattern, cur_text.text):
            return True
        return False

    def _special_text_rect(self):
        """特殊矩形框　　不需要再合并"""
        for i in range(self._len_rect):
            if i in self.usedIds:
                continue
            # 剔除各种特殊情况下的文本框
            state2 = self.__is_abnormal__ratio(self._sorted_ocr_result[i])
            if state2:
                self.usedIds.append(i)
                continue

            # 剔除尾部特有数据
            state3 = self.__is_irrelevant(self._sorted_ocr_result[i])
            if state3:
                if self._len_rect - i <= 3:
                    self.usedIds.extend(range(i, self._len_rect))
                    break
                self.usedIds.append(i)

    def _common_candidates_bbox(self, field_key, center_id, count):
        logger.debug("开始合并文本框")
        init_candidate_ids = [_id for _id in range(max(center_id - count, 0), min(center_id + count, self._len_rect)) if
                              _id not in self.usedIds]
        center = self._sorted_ocr_result[center_id]
        filter_candidate_ids = []
        for _id in init_candidate_ids:
            candidate = self._sorted_ocr_result[_id]
            # 1. 如果候选框在中心点左侧，直接Pass
            if field_key != "businessScope":
                if center.left_point - candidate.left_point >= 10:
                    continue
            else:
                if center.left_point - candidate.left_point >= 35:
                    continue

            # 2. 候选框在中心点的上面时，候选框必须靠近中心点的右侧，即1/3及其右侧
            if center.bottom_point > candidate.bottom_point and \
                    candidate.left_point <= center.left_point + center.width * 2 / 3:
                continue

            # 高度间隔太大,　宽度差太大，　文本高度不一致
            cp0 = ComparisonPosition(center, candidate)
            if cp0.height_gap_ratio > 10 or cp0.width_gap_ratio > 10 or cp0.height_ratio > 3:
                continue

            filter_candidate_ids.append(_id)
        return filter_candidate_ids

    def _set_ratio_params(self):
        first_width_gap_ratio, center_height_ratio, other_width_gap_ratio = 1, 0.2, 0.2
        if self._certificateName in [YPSCXKZ, YPJYXKZ]:
            first_width_gap_ratio = 0.5
        elif self._certificateName in [YLQXSCXKZ, YLQXJYXKZ]:
            first_width_gap_ratio = 0.8
        elif self._certificateName == YLQXJYBAPZ:
            first_width_gap_ratio = 5
        elif self._certificateName == YLQXSCBAPZ:
            first_width_gap_ratio = 1
        elif self._certificateName == YPZCZ:
            first_width_gap_ratio = 1
        elif self._certificateName == YLQXZCZ:
            first_width_gap_ratio = 1
        elif self._certificateName == YLQXBAZ:
            first_width_gap_ratio = 0.5
        return first_width_gap_ratio, center_height_ratio, other_width_gap_ratio

    @staticmethod
    def __maybe_real_text(field_key, candidate_text):
        if field_key == "supplierName":
            if len(candidate_text) >= 15 and not re.search("公司|社区", candidate_text):
                return False
        elif field_key == "certificateCode":
            if len(candidate_text) >= 10 and not re.search("[0-9a-zA-Z]", candidate_text):
                return False
        elif field_key == "legalRepresentative" and len(candidate_text) >= 5:
            return False
        elif field_key == "certificateValidity" and not re.search("[年月日\\d{4}]", candidate_text):
            return False
        elif field_key == "issueDate" and not re.search("[年月日\\d{4}]", candidate_text):
            return False
        elif field_key == "manufacturer" and len(candidate_text) >= 30:
            return False
        elif field_key == "productName" and len(candidate_text) >= 30:
            return False
        elif field_key == "productSpecification" and len(candidate_text) >= 30:
            return False
        elif field_key == "productValidity" and len(candidate_text) >= 30:
            return False
        elif field_key == "registerRepresentative" and len(candidate_text) >= 30:
            return False
        elif field_key == "productAddress" and len(candidate_text) >= 40:
            return False
        elif field_key == "dosageForm" and len(candidate_text) >= 15:
            return False
        elif field_key == "registerAddress" and len(candidate_text) >= 40:
            return False
        elif field_key == "applicationScope" and len(candidate_text) >= 40:
            return False
        return True

    def _single_line_fine_filter(self, field_key, center, candidate_ids):
        """单行"""
        fine_filter_ids = []

        # 以水平间隔距离进行排序
        def _left_distance(x):
            dis = self._sorted_ocr_result[x].left_point - center.right_point
            if dis <= -10:
                return math.inf
            return dis
        width_gap_1, center_height_ratio, width_gap_2 = self._set_ratio_params()
        sorted_candidate_ids = sorted(candidate_ids, key=lambda x: _left_distance(x), reverse=False)

        for _id in sorted_candidate_ids:
            candidate = self._sorted_ocr_result[_id]
            # =================================通用基础验证====================================#
            # 候选框和中心框高度差距，候选框和中心狂水平位置关系
            if candidate.left_point < center.right_point-10:
                continue
            if center.height * 1.5 < candidate.height:
                continue
            if ComparisonPosition(center, candidate).height_gap_ratio > 0:
                continue
            # =================================通用基础验证====================================#
            if len(fine_filter_ids) == 0:
                cp = ComparisonPosition(center, candidate)
                if cp.width_gap_ratio > width_gap_1:
                    break
            else:
                cp = ComparisonPosition(self._sorted_ocr_result[fine_filter_ids[-1]], candidate)
                if cp.width_gap_ratio > width_gap_2:
                    break

            if len(fine_filter_ids) > 0 and cp.center_height_ratio > center_height_ratio:
                continue

            if re.search("[:：;；]", center.text) and re.search("[:：;；]", candidate.text):
                break
            if not self.__maybe_real_text(field_key, candidate.text):
                continue
            fine_filter_ids.append(_id)
        return fine_filter_ids

    def common_single_line_filter(self, field_key, center_id, candidate_ids):
        """生产厂家"""
        center_point = self._sorted_ocr_result[center_id]
        center = self._sorted_ocr_result[center_id]
        center_text = center_point.text
        candidate_ids = [_id for _id in candidate_ids if _id not in self.usedIds]
        fine_ids = self._single_line_fine_filter(field_key, center, candidate_ids)

        for _id in fine_ids:
            center_text += self._sorted_ocr_result[_id].text
            self.usedIds.append(_id)

        return center_text

    def common_multi_lines_filter(self, center_id, candidate_ids):
        """经营范围"""
        center_point = self._sorted_ocr_result[center_id]
        candidate_ids = [_id for _id in candidate_ids if _id not in self.usedIds]
        scope_text = center_point.text
        if len(candidate_ids) == 0:
            return scope_text

        filter_candidate_ids = []
        for _id in candidate_ids:
            candidate = self._sorted_ocr_result[_id]
            if center_point.top_point > candidate.bottom_point and candidate.left_point <= center_point.right_point:
                continue
            filter_candidate_ids.append(_id)

        mp = MergeParser(
            sorted_ocr_result=self._sorted_ocr_result,
            candidate_ids=filter_candidate_ids,
            used_ids=self.usedIds,
            center_id=center_id
        )

        scope_text += mp.merge_rect()
        return scope_text

    def mdr_regular_expression(self):
        """医疗器械备案凭证"""
        merged_text_lst = []
        for _id in range(self._len_rect - 1):
            if _id in self.usedIds:
                continue
            init_rect = self._sorted_ocr_result[_id]
            self.usedIds.append(_id)
            merge_text = init_rect.text
            for inner_id in range(_id + 1, self._len_rect):
                cp = ComparisonPosition(init_rect, self._sorted_ocr_result[inner_id])
                if cp.height_gap_ratio >= 2:
                    break
                self.usedIds.append(inner_id)
                merge_text += self._sorted_ocr_result[inner_id].text
                init_rect = self._sorted_ocr_result[inner_id]
            merged_text_lst.append(merge_text)
        return merged_text_lst
